# pybotwar ubertweak tournament web server

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import cgi
import datetime
import mimetools
import os
import re
import urlparse

import conf
import stats
import util
import viewselect

viewselect.select_view_module('none')
view = viewselect.get_view_module()

from game import Game
import world

robotdir = util.get_robot_dirs()[0]

# Get the user
def get_owner():
    return ''


# The webserver
class pybotwarserver(BaseHTTPRequestHandler):
    def do_GET(self):
        
        output = ''
        mimetype = 'text/html'
        
        request = urlparse.urlparse(self.path)
        queries = urlparse.parse_qs(request.query)
        path = re.search('\w+', request.path) 
        method = path and path.group(0).upper() or ''
        
        if method == 'DOWNLOAD' and 'name' in queries:
            owner = 'owner' in queries and queries['owner'][0] or ''
            robotname = queries['name'][0]
            output = file(os.path.join(robotdir, owner + '_' + robotname + '.pyc')).read()
            mimetype = 'application/x-pybot'
            self.send_response(200)
            self.send_header('Content-Disposition', 'attachment; filename="' + owner + '_' + robotname + '.pybot"')
            self.end_headers()
            self.wfile.write(output)
            return
        
        # The main window!
        else:
            output += '''
<!doctype html>
<head>
    <title>pybotwar tournament server</title>
    <style>
td, th { vertical-align: top; padding: 0 20px; text-align: left; }
    </style>
</head>
<body>
    <h1>pybotwar tournament server</h1>
    <table border="0" width="100%">
        <tr>
<td>
    <h2>Rankings</h2>
    <table>
        <tr><th>Owner</th><th>Robot</th><th>Score</th><th>Last update</th><th></th></tr>
'''
        
            # Get the stats!
            stats.dbopen()
            # Get the stats
            q = '''
            SELECT
                program_name,
                owner,
                mean,
                lastupdate

            FROM stats
            ORDER BY mean DESC
            '''
            stats.c.execute(q, locals())
            results = stats.c.fetchall()
            stats.dbclose()
            
            for robot in results:
                output += '''<tr><td>%s</td><td>%s</td><td>%d</td><td>%s</td>''' % (robot[1], robot[0], robot[2], robot[3])
                if not robot[1] == '':
                    output += '''<td><a href="/download?owner=%s&name=%s">Download</a></td>''' % (robot[1], robot[0])
                output += '''</tr>\n'''
        
            output += '''
    </table>
</td>
<td>'''

            if get_owner() == '':
                # TODO add link
                output += '''
    <h2>Please log in!</h2>'''
    
    
            else:
                output += '''
    <h2>Upload your robot</h2>
    <form action="/" method="POST" enctype="multipart/form-data">
        <input type="file" name="data">
        <input type="submit" value="Upload!">
    </form>'''
    
            output += '''
</td>
        </tr>
    </table>
</body>
'''
        
        self.send_response(200)
        self.send_header('Content-type', mimetype)
        self.end_headers()
        self.wfile.write(output)



    # Upload!        
    def do_POST(self):
        
        # Get owner
        owner = get_owner()
        
        if owner == '':
            self.make_error("Please login to the Ubertweak website first")
            return
        
        # Get the file data and name
        ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
        query = my_parse_multipart(self.rfile, pdict)
        filedata = query.get('data')[0]
        filename = query.get('data')[1]
        filename_noext, ext = os.path.splitext(filename)
        
        if filename == '':
            self.make_error("The file, you forgot it")
            return
        
        # Write the file
        destpath = os.path.join(robotdir, owner + '_' + filename)
        myfile = open(destpath, 'w')
        myfile.write(filedata)
        myfile.close()
        
        # Add the robot to the database
        lastupdate = datetime.datetime.now().strftime('%A %H:%M')
        if stats.exists(filename_noext, owner):
            stats.makequery('''\
            UPDATE stats
            SET playcount = 0,
                lastupdate = :lastupdate,
                buggy = 0
            WHERE
                program_name = :filename_noext AND
                owner = :owner''', (lastupdate, filename_noext, owner))
        else:
            stats.makequery('''\
            INSERT INTO stats
                (program_name,
                    owner,
                    buggy,
                    lastupdate,
                    matches,
                    playcount,
                    mean,
                    stdev,
                    wins,
                    opponents,
                    kills,
                    damage_caused)
                VALUES
                    (:filename_noext,
                        :owner,
                        0,
                        :lastupdate,
                        0,
                        0,
                        1500,
                        350,
                        0,
                        0,
                        0,
                        0)''', (filename_noext, owner, lastupdate))
        
        # Test the robot!
        try:
            conf.maxtime = 5
            conf.robots = [
                owner + '_' + filename_noext,
                'rock'
            ]
            dt = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            game = Game(False, dt)
            game.run()
            world.Robot.nrobots = 0
            view.Robot.nrobots = 0
            if game.rnd == 0:
                raise
        except:
            stats.makequery('''\
            UPDATE stats
            SET buggy = 1
            WHERE
                program_name = :filename_noext AND
                owner = :owner''', (filename_noext, owner))
                
            self.make_error("Your robot broke the interwebs")  
            return
        
        # Show a pretty page
        output = '''
<!doctype html>
<head>
    <title>pybotwar tournament server</title>
    <style>
td { vertical-align: top }
    </style>
</head>
<body>
    <h1>pybotwar tournament server</h1>
    <p>Your robot ''' + filename + ''' has been uploaded! Hurrah!
    <p><a href="/">Go back</a>
</body>'''
        
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        self.wfile.write(output)

    def make_error(self, message):
        # Login first!
        output = '''
<!doctype html>
<head>
    <title>pybotwar tournament server</title>
</head>
<body>
    <h1>pybotwar tournament server</h1>
    <p>%s :(
    <p><a href="/">Go back</a>
</body>''' % message
        
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        self.wfile.write(output)
        return



def my_parse_multipart(fp, pdict):
    """Parse multipart input.

    Arguments:
    fp   : input file
    pdict: dictionary containing other parameters of content-type header

    Returns a dictionary just like parse_qs(): keys are the field names, each
    value is a list of values for that field.  This is easy to use but not
    much good if you are expecting megabytes to be uploaded -- in that case,
    use the FieldStorage class instead which is much more flexible.  Note
    that content-type is the raw, unparsed contents of the content-type
    header.

    XXX This does not parse nested multipart parts -- use FieldStorage for
    that.

    XXX This should really be subsumed by FieldStorage altogether -- no
    point in having two implementations of the same parsing algorithm.
    Also, FieldStorage protects itself better against certain DoS attacks
    by limiting the size of the data read in one chunk.  The API here
    does not support that kind of protection.  This also affects parse()
    since it can call parse_multipart().

    """
    boundary = ""
    if 'boundary' in pdict:
        boundary = pdict['boundary']
    if not cgi.valid_boundary(boundary):
        raise ValueError,  ('Invalid boundary in multipart form: %r'
                            % (boundary,))

    nextpart = "--" + boundary
    lastpart = "--" + boundary + "--"
    partdict = {}
    terminator = ""

    while terminator != lastpart:
        bytes = -1
        data = None
        if terminator:
            # At start of next part.  Read headers first.
            headers = mimetools.Message(fp)
            clength = headers.getheader('content-length')
            if clength:
                try:
                    bytes = int(clength)
                except ValueError:
                    pass
            if bytes > 0:
                if maxlen and bytes > maxlen:
                    raise ValueError, 'Maximum content length exceeded'
                data = fp.read(bytes)
            else:
                data = ""
        # Read lines until end of part.
        lines = []
        while 1:
            line = fp.readline()
            if not line:
                terminator = lastpart # End outer loop
                break
            if line[:2] == "--":
                terminator = line.strip()
                if terminator in (nextpart, lastpart):
                    break
            lines.append(line)
        # Done with part.
        if data is None:
            continue
        if bytes < 0:
            if lines:
                # Strip final line terminator
                line = lines[-1]
                if line[-2:] == "\r\n":
                    line = line[:-2]
                elif line[-1:] == "\n":
                    line = line[:-1]
                lines[-1] = line
                data = "".join(lines)
        line = headers['content-disposition']
        if not line:
            continue
        key, params = cgi.parse_header(line)
        if key != 'form-data':
            continue
        if 'name' in params:
            name = params['name']
        else:
            continue
        if name in partdict:
            partdict[name].append(data)
        else:
            partdict[name] = [data, params['filename']]

    return partdict



def start(port=2080):
    try:
        print 'Starting pybotwar web server on port ' + str(port) ;

        logfile = open('weblog.txt', 'a', 0)
        server = HTTPServer(('', port), pybotwarserver)
        server.serve_forever()
    except KeyboardInterrupt:
        print '\nStopping pybotwar web server'
        server.socket.close()
        logfile.close()

if __name__ == '__main__':
    #start(80)
    start()
